package com.well.register.data;

import com.alibaba.fastjson.JSONObject;
import com.well.register.event.ServerHandler;
import com.well.register.util.ThreadUtils;
import com.well.rpc.model.ClusterServerModel;
import com.well.rpc.model.EventType;
import com.well.rpc.model.RegisterModel;
import com.well.rpc.model.TransData;
import io.netty.channel.ChannelHandlerContext;
import org.apache.commons.lang3.RandomUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;

public class DataCenter {

    private static Logger logger = LoggerFactory.getLogger(DataCenter.class);
    /* 本机 ip */
    private static String host;
    /* 本机 端口 */
    private static Integer port;
    /* 服务启动时间 */
    private static Long startTime;
    /* 是否开启心跳日志 */
    private static Boolean enableHeatBeat;
    /*此数据结构模型 将注册的服务以NameSpace、Group 进行隔离。*/
    private static ConcurrentHashMap<String, HashMap<String, List<RegisterModel>>> serverModelMap = new ConcurrentHashMap<>();
    /* 反向查找 */
    private static ConcurrentHashMap<String, RegisterModel> registers = new ConcurrentHashMap<>();
    /* 保存所有的channel信息, 为了后面在通道上进行数据操作 */
    private static ConcurrentHashMap<String, HashMap<String, HashMap<String, List<ChannelHandlerContext>>>> channelHandlers
            = new ConcurrentHashMap<>();

    /* 存放集群信息 */
    private static Map<String, ClusterServerModel> clusterServerModels;

    public static Map getClusterServerModels() {
        return clusterServerModels;
    }

    public static void setClusterServerModels(Map<String, ClusterServerModel> clusterServerModels) {
        DataCenter.clusterServerModels = clusterServerModels;
    }

    /*存储定时任务future信息*/
    private static Map<String, ScheduledFuture> scheduledFutures = new HashMap<>();

    /**
     * 通过namespace获取注册服务列表
     *
     * @param ns namespace
     * @return group 数据
     */
    private static HashMap<String, HashMap<String, List<ChannelHandlerContext>>> getNameSpaceChannelLists(String ns) {
        return channelHandlers.computeIfAbsent(ns, k -> new HashMap<>());
    }

    /**
     * 获取组数据
     *
     * @param groups
     * @param group
     * @return
     */
    private static HashMap<String, List<ChannelHandlerContext>> getGroupChannelLists(HashMap<String, HashMap<String,
            List<ChannelHandlerContext>>> groups, String group) {
        return groups.computeIfAbsent(group, k -> new HashMap<>());
    }

    /**
     * 获取组数据
     *
     * @param ns    namespace
     * @param group group
     * @return 获取注册channel列表
     */
    private static HashMap<String, List<ChannelHandlerContext>> getGroupChannelLists(String ns, String group) {
        HashMap<String, HashMap<String, List<ChannelHandlerContext>>> groups = getNameSpaceChannelLists(ns);

        return getGroupChannelLists(groups, group);
    }


    /**
     * 获取组数据
     *
     * @param ns         namespace
     * @param group      group
     * @param serverName serverName
     * @return 获取注册channel列表
     */
    private static List<ChannelHandlerContext> getServerChannelLists(String ns, String group, String serverName) {
        HashMap<String, List<ChannelHandlerContext>> groupChannelLists = getGroupChannelLists(ns, group);
        return groupChannelLists.computeIfAbsent(serverName, k -> new ArrayList<>());

    }

    /**
     * 注册服务
     *
     * @param registerModel         注册数据模型
     * @param channelHandlerContext 通道上下文
     * @return 返回值，用于判断是否注册成功
     */
    public static boolean registerChannel(RegisterModel registerModel, ChannelHandlerContext channelHandlerContext) {
        String remoteAddress = ServerHandler.getRemoteAddress(channelHandlerContext);
        if (registers.get(remoteAddress) != null) {
            logger.error("this channel is registered, " + remoteAddress);
            return false;
        }
        List<ChannelHandlerContext> serverChannelLists = getServerChannelLists(
                registerModel.getNameSpace(), registerModel.getGroup(), registerModel.getServerName());
        serverChannelLists.add(channelHandlerContext);
        //反向注册
        registers.put(remoteAddress, registerModel);
        return true;
    }

    /**
     * 根据 ns获取注册数据模型
     *
     * @param ns namespace
     * @return 返回group数据
     */
    private static HashMap<String, List<RegisterModel>> getGroupRegisterModelByNameSpace(String ns) {
        HashMap<String, List<RegisterModel>> groups = serverModelMap.get(ns);
        if (groups == null) {
            groups = new HashMap<>();
        }
        return groups;
    }

    /**
     * 根据ns 和 group获取数据模型
     *
     * @param ns    namespace
     * @param group group
     * @return 注册数据模型
     */
    public static List<RegisterModel> getGroupListRegisterModel(String ns, String group) {
        HashMap<String, List<RegisterModel>> groups = getGroupRegisterModelByNameSpace(ns);
        List<RegisterModel> serverModels = groups.get(group);
        if (serverModels == null) {
            serverModels = new ArrayList<>();
        }
        return serverModels;
    }

    /**
     * 获取GROUP数据模型
     *
     * @param groups GROUPS
     * @param group  group
     * @return 注册数据模型
     */
    public static List<RegisterModel> getGroupListRegisterModel(HashMap<String, List<RegisterModel>> groups, String group) {
        List<RegisterModel> serverModels = groups.get(group);
        if (serverModels == null) {
            serverModels = new ArrayList<>();
        }
        return serverModels;
    }

    /**
     * 注册服务模型
     *
     * @param registerModel
     */
    public static void registerServerModel(RegisterModel registerModel) {
        HashMap<String, List<RegisterModel>> groups = getGroupRegisterModelByNameSpace(registerModel.getNameSpace());
        List<RegisterModel> serverModels = getGroupListRegisterModel(groups, registerModel.getGroup());
        serverModels.add(registerModel);
        groups.put(registerModel.getGroup(), serverModels);
        serverModelMap.put(registerModel.getNameSpace(), groups);
    }

    /**
     * 获取注册在服务中心上的所有服务信息
     *
     * @return map
     */
    public static ConcurrentHashMap<String, HashMap<String, List<RegisterModel>>> getServerModels() {
        return serverModelMap;
    }

    /**
     * 获取 remote address 对应的 RegisterModel
     *
     * @param remoteAddress address
     * @return model
     */
    public static RegisterModel getRegisterModel(String remoteAddress) {
        return registers.get(remoteAddress);
    }

    /**
     * 所有节点数据同步
     *
     * @param model
     */
    public static void notifyAllNode(RegisterModel model, List<RegisterModel> serverModels) {
        HashMap<String, List<ChannelHandlerContext>> serverChannelLists = getGroupChannelLists(model.getNameSpace(), model.getGroup());
        if (serverChannelLists.size() < 1) {
            logger.info("namespace :" + model.getNameSpace() + " ; group :" + model.getGroup() + "; 已无服务注册 ！");
            getNameSpaceChannelLists(model.getNameSpace()).remove(model.getGroup());
            return;
        }
        ScheduledExecutorService threadPool = ThreadUtils.scheduledThreadPool();
        TransData transData = TransData.transData(EventType.REGISTER_RESPONSE, JSONObject.toJSONString(serverModels));
        int count = 0;
        for (Map.Entry<String, List<ChannelHandlerContext>> entry : serverChannelLists.entrySet()) {
            if (entry.getValue().size() == 0) {
                serverChannelLists.remove(entry.getKey());
            }
            for (ChannelHandlerContext context : entry.getValue()) {
                if (context.isRemoved()) {
                    context.close();
                    continue;
                }
                threadPool.submit(() -> {
                    context.writeAndFlush(transData);
                });
                count++;
            }
        }
        logger.info(count + " server complete synchronously！");
    }

    /**
     * @param handler
     */
    public static synchronized void removeServer(ChannelHandlerContext handler) {
        //获取注册模型
        String remoteAddress = ServerHandler.getRemoteAddress(handler);
        RegisterModel model = registers.get(remoteAddress);
        if (model == null) {
            logger.error("Invalid Connect :" + remoteAddress);
            return;
        }
        registers.remove(remoteAddress);
        //移除handler
        List<ChannelHandlerContext> channelLists = getServerChannelLists(
                model.getNameSpace(), model.getGroup(), model.getServerName());
        channelLists.remove(handler);
        if (channelLists.size() == 0) {
            HashMap<String, List<ChannelHandlerContext>> groupChannelLists = getGroupChannelLists(model.getNameSpace(), model.getGroup());
            groupChannelLists.remove(model.getServerName());
            if (groupChannelLists.size() == 0) {
                HashMap<String, HashMap<String, List<ChannelHandlerContext>>> nameSpaceChannelLists =
                        getNameSpaceChannelLists(model.getNameSpace());
                nameSpaceChannelLists.remove(model.getGroup());
                if (nameSpaceChannelLists.size() == 0) {
                    channelHandlers.remove(model.getNameSpace());
                }
            }
        }
        // 移除serverModel
        List<RegisterModel> serverModels = getGroupRegisterModelByNameSpace(model.getNameSpace()).get(model.getGroup());
        serverModels.remove(model);
        if (serverModels.size() == 0) {
            HashMap<String, List<RegisterModel>> groupRegisterModelByNameSpace = getGroupRegisterModelByNameSpace(model.getNameSpace());
            groupRegisterModelByNameSpace.remove(model.getGroup());
            if (groupRegisterModelByNameSpace.size() == 0) {
                serverModelMap.remove(model.getNameSpace());
            }
        }
        notifyAllNode(model, serverModels);
    }

    /**
     * get channelHandler
     *
     * @param ns         ns
     * @param group      group
     * @param serverName server name
     * @return context
     */
    public static ChannelHandlerContext getChannelHandlerContext(String ns, String group, String serverName) {
        List<ChannelHandlerContext> channelLists = getServerChannelLists(ns, group, serverName);
        if (channelLists.size() == 0) {
            return null;
        }
        int index = RandomUtils.nextInt(0, channelLists.size());
        return channelLists.get(index);
    }

    /**
     * get channelHandler
     *
     * @return context
     */
    public static ChannelHandlerContext getChannelHandlerContext(RegisterModel registerModel) {
        List<ChannelHandlerContext> channelLists = getServerChannelLists(registerModel.getNameSpace(), registerModel.getGroup(), registerModel.getServerName());
        for (ChannelHandlerContext ctx : channelLists) {
            if (ServerHandler.getRemoteAddress(ctx).equals(registerModel.getSubThreadAddr())) {
                return ctx;
            }
        }
        return null;
    }


    public static String getHost() {
        return host;
    }

    public static synchronized void setHost(String ip) {
        host = ip;
    }

    public static Integer getPort() {
        return port;
    }

    public static synchronized void setPort(Integer portPara) {
        port = portPara;
    }

    public static Long getStartTime() {
        return startTime;
    }

    public static void setStartTime(Long startTime) {
        DataCenter.startTime = startTime;
    }

    public static Boolean getEnableHeatBeat() {
        return enableHeatBeat;
    }

    public static void setEnableHeatBeat(Boolean enableHeatBeat) {
        DataCenter.enableHeatBeat = enableHeatBeat;
    }

    /**
     * 保存scheduleFuture
     *
     * @param jobInfo jobInfo
     * @param future  future
     */
    public static void saveScheduleFuture(String jobInfo, ScheduledFuture future) {
        scheduledFutures.put(jobInfo, future);
    }

    /**
     * 查询future
     *
     * @param jobInfo
     * @return
     */
    public static ScheduledFuture getScheduleFuture(String jobInfo) {
        return scheduledFutures.get(jobInfo);
    }
    /**
     * 异常future
     *
     * @param jobInfo
     * @return
     */
    public static void removeScheduleFuture(String jobInfo) {
         scheduledFutures.remove(jobInfo);
    }

}
